import { hex_md5,rstr_md5} from './md5'
import {CODE_ERROR_CMD_WEBSOCKET} from "../QtBaseController";
import {GetQtController} from "../QtUtils";

class HCRect{
  constructor(x, y, w, h)
	{
		this.x = x;
		this.y = y;
		this.w = w;
		this.h = h;
	}
}

class HCTexture{
    constructor(gl)
	{
		this.gl = gl;
		this.texture = gl.createTexture();
		gl.bindTexture(gl.TEXTURE_2D, this.texture);

		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    }

	bind(n, program, name)
	{
		var gl = this.gl;
		gl.activeTexture([gl.TEXTURE0, gl.TEXTURE1, gl.TEXTURE2][n]);
		gl.bindTexture(gl.TEXTURE_2D, this.texture);
		gl.uniform1i(gl.getUniformLocation(program, name), n);
	}

    fill(width, height, data)
	{
		var gl = this.gl;
		gl.bindTexture(gl.TEXTURE_2D, this.texture);
		gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, width, height, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, data);
	}


}

export default
class HCPlayer{
	constructor()
	{

		this.asd = new Uint8Array();
		this.id = "123";
		this.remote_port=8091;
		this.remote_ip="192.168.1.103";

		this.keyPressedList = [];
		this.rectWidth = 100;

		this.canvas = null;
		this.ctx = null;
		this.cursor = null;
		this.cursorField = null;
		this.cursorContainer = null;
		this.canvasRect=null;
		this.cursorPosX = 100;
		this.cursorPosY = 100;
		this.width = 1;
		this.height = 1;

		this.isScaling = false;
		this.scaleSize = 0;
		this.scaleWidth=1920;
		this.scaleHeight=1080;
		this.originWidth=1920;
		this.originHeight=1080;

		this.login=null;
		this.pass=null;
		this.workerPath="./";
		this.bSetupMouse=true;   //setup mouse on the init;

		this.deltaRect = new HCRect(0,0,0,0);
		this.isMobilePhone  = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);

		this.isConnected = false;
		this.cmdTmp = new Uint8Array([]);
		this.dataTmp = new Uint8Array([]);
		this.loginClass = null;

		this.messageField = null;

		this.webCmdSocket=null;


		this.KEY_GET_IMAGE = new Uint8Array([71,73,77,71]);     //GIMG
		this.KEY_BROWSER_IMAGE = new Uint8Array([66,73,77,71]);     //BIMG
		this.KEY_SET_SCALE_SIZE =new Uint8Array([83,67,83,90]); //SCSZ
		this.KEY_SET_CURSOR_POS = new Uint8Array([83,67,85,80]); //SCUP
		this.KEY_SET_CURSOR_DELTA = new Uint8Array([83,67,85,68]); //SCUD
		this.KEY_SET_MOUSE_KEY = new Uint8Array([83,77,75,83]); //SMKS
		this.KEY_SET_MOUSE_WHEEL = new Uint8Array([83,77,87,72]); //SMWH
		this.KEY_SET_KEY_STATE = new Uint8Array([83,75,83,84]);   //SKST
		this.KEY_CHANGE_DISPLAY = new Uint8Array([67,72,68,80]);  //CHDP
		this.KEY_TILE_RECEIVED = new Uint8Array([84,76,82,68]);   //TLRD
		this.KEY_SET_AUTH_REQUEST = new Uint8Array([83,65,82,81]); //SARQ

		this.KEY_HEARTBEAT = new Uint8Array([72,69,65,84]); //HEAT

		this.KEY_DEBUG = new Uint8Array([68,66,85,71]); //DBUG;
		this.KEY_IMAGE_PARAM = "73,77,71,80"; //IMGP
		//this.KEY_HEARTBEAT = "72,69,65,84";  //HEAT
		this.KEY_SET_NONCE = "83,84,78,67";   //STNC
		this.KEY_SET_AUTH_RESPONSE = "83,65,82,80"; //SARP
		this.KEY_CHECK_AUTH_RESPONSE = "67,65,82,80"; //CARP
		this.KEY_SET_NAME = "83,84,78,77";   //STNM
		this.KEY_AUTO_LOGIN = "65,85,76,71";       //AULG


		this.COMMAD_SIZE = 4;
		this.REQUEST_MIN_SIZE = 6;
        this.isInitEnv=false;
		this.isHCNative=false;

		this.fnMousePosCallback = null;   //鼠标位置的回调，判断是否可操作三维，返回true 可操作，false 不可操作，忽略
    this.fnWebSocketCallback = null;   //webSocket 连接结果
    }

    extraKeyStateChanged(key,state,event){
        var num = key.num;

        if(state)
            key.style.backgroundColor = "#aaa";
        else key.style.backgroundColor = "#222";

        if(num === "1111")
        {if(state)this.sendToSocket(this.KEY_CHANGE_DISPLAY);}
        else if(num === "1112")
        {if(state)this.openFullscreen();}
        else if(num === "1113")
        {if(state)this.showKeyboard();}
        else {this.sendParameters(this.KEY_SET_KEY_STATE,num,state);}
    }

    openFullscreen()
    {
        if (document.body.requestFullscreen) {
            document.body.requestFullscreen();
        } else if (document.body.mozRequestFullScreen) { /* Firefox */
            document.body.mozRequestFullScreen();
        } else if (document.body.webkitRequestFullscreen) { /* Chrome, Safari and Opera */
            document.body.webkitRequestFullscreen();
        } else if (document.body.msRequestFullscreen) { /* IE/Edge */
            document.body.msRequestFullscreen();
        } else if (document.body.webkitEnterFullScreen) {
            document.body.webkitEnterFullScreen();
        }
    }

	getBrowserInfo()
	{
		var agent = navigator.userAgent.toLowerCase() ;
		 
		var regStr_ie = /msie [\d.]+;/gi ;
		var regStr_ff = /firefox\/[\d.]+/gi
		var regStr_chrome = /chrome\/[\d.]+/gi ;
		var regStr_saf = /safari\/[\d.]+/gi ;
		//IE
		if(agent.indexOf("msie") > 0)
		{
			return agent.match(regStr_ie) ;
		}
		 
		//firefox
		if(agent.indexOf("firefox") > 0)
		{
			return agent.match(regStr_ff) ;
		}
		 
		//Safari
		if(agent.indexOf("safari") > 0 && agent.indexOf("chrome") < 0)
		{
			return agent.match(regStr_saf) ;
		}
		 
		//Chrome
		if(agent.indexOf("chrome") > 0)
		{
			return agent.match(regStr_chrome) ;
		}
	    return agent;
	}

    createExtraKeysHtml()
    {
        this.topMenu = document.createElement('div');
        this.topMenu.id = 'topMenu';
        this.topMenu.style.cssText = 'position:absolute;\
            background-color: rgba(47,47,47,100);\
            border: 1px solid #444;\
            border-radius: 3px;\
            top: 0px;\
            left: calc((100% / 2) - 13px);\
            width: 26px;\
            height: 26px;\
            z-index: 3;\
            visibility: visible;';

        var cssExtraKey = 'position: absolute;\
            border: 1px solid #aaa;\
            width: 24px;\
            height: 24px;\
            background-color: #222;\
            border-radius: 3px;\
            color: #eee;\
            text-align: center;\
            line-height: 24px;\
            font-size: 14px;';


        this.btnFullscrean = document.createElement('div');
        this.btnFullscrean.id = 'btnFullscrean';
        this.btnFullscrean.classList.add('extraKey');
        this.btnFullscrean.num = '1112';
        this.btnFullscrean.style.cssText = cssExtraKey + 'width: 28px; right: 0px;';
        this.btnFullscrean.innerHTML = '<svg width="24" height="24" viewBox="0 0 24 24"> \
                <path d="M24 9h-4v-5h-5v-4h9v9zm-9 15v-4h5v-5h4v9h-9zm-15-9h4v5h5v4h-9v-9z \
                m9-15v4h-5v5h-4v-9h9z" fill="white"/></svg>';
        //this.topMenu.append(this.btnFullscrean);

        //document.body.append(this.topMenu);

        var extraKeys = document.getElementsByClassName('extraKey');
        var isMobilePhone = /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent);

        if(isMobilePhone)
        {
            for(var j=0;j<extraKeys.length;++j) {
                extraKeys[j].addEventListener('touchstart', this.extraKeyStateChanged.bind(this,extraKeys[j],true));
                extraKeys[j].addEventListener('touchend', this.extraKeyStateChanged.bind(this,extraKeys[j],false));
            }
        }
        else
        {
            for(var i=0;i<extraKeys.length;++i) {
                extraKeys[i].addEventListener('mousedown', this.extraKeyStateChanged.bind(this,extraKeys[i],true));
                extraKeys[i].addEventListener('mouseup', this.extraKeyStateChanged.bind(this,extraKeys[i],false));
            }
        }
    }

    setupTouch()
    {
        this.touchX = 0;
        this.touchY = 0;
        this.touchDistance = 0;

        this.stepPress = 0;
        this.stepRelease = 0;
        this.stepMove = 0;
        this.isPressed = false;

        this.touchTimer = null;

        this.cursorField.addEventListener("touchstart", this.touchPress.bind(this));
        this.cursorField.addEventListener("touchmove", this.touchMove.bind(this));
        this.cursorField.addEventListener("touchend", this.touchRelease.bind(this));

        this.cursor.style.visibility = "visible";
    }

    touchPress(e)
    {
        //e.preventDefault();

        if(e.touches.length === 1 && !this.isScaling)
        {
            this.touchX = e.touches[0].pageX;
            this.touchY = e.touches[0].pageY;

            this.stepPress += 1;
            this.touchCounter();
        }
    }

    touchMove(e)
    {
        //e.preventDefault();

        if(e.touches.length === 1 && !this.isScaling)
        {
            this.stepMove += 1;
            this.touchCounter();

            if(this.stepPress === 1 && this.stepMove === 1 && this.stepRelease === 0)
            {
                this.isPressed = false;
            }
            else if(this.stepPress === 2 && this.stepMove === 1 && this.stepRelease === 1)
            {
                this.isPressed = true;
                this.sendParameters(this.KEY_SET_MOUSE_KEY,0,1);
            }

            var x = e.touches[0].pageX;
            var y = e.touches[0].pageY;

            this.calcCursorPos(x, y);
        }
        else if(e.touches.length === 2)
        {
            this.isScaling = true;

            if(this.touchTimer)
                clearTimeout(this.touchTimer);

            var x1 = e.touches[0].pageX;
            var y1 = e.touches[0].pageY;

            var x2 = e.touches[1].pageX;
            var y2 = e.touches[1].pageY;

            var distance = Math.sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));

            if(distance > this.touchDistance)
                this.scaleSize += 0.5;
            if(distance < this.touchDistance)
                this.scaleSize -= 0.5;
            if(this.scaleSize < 0)
                this.scaleSize = 0;

            this.touchDistance = distance;

            this.updatePositions();
        }
    }

    touchRelease(e)
    {
        //e.preventDefault();

        if(!this.isScaling)
        {
            this.stepRelease += 1;
            this.touchCounter();
        }
        else
        {
            this.touchDistance = 0;
            this.isScaling = false;
        }

        if(this.isPressed)
            this.sendParameters(this.KEY_SET_MOUSE_KEY,0,0);
    }

    touchCounter()
    {
        if(this.touchTimer)
            clearTimeout(this.touchTimer);

        this.touchTimer = setTimeout(this.touchWaiter.bind(this),200);
    }

    touchWaiter()
    {
        if(this.touchTimer)
        {
            clearTimeout(this.touchTimer);
            this.touchTimer = null;
        }

        if(this.stepPress === 1 && this.stepMove === 0 && this.stepRelease === 0)
        {
            this.touchTimer = setTimeout(this.touchWaiter.bind(this),400);
            this.stepPress = 0;
            return;
        }
        else if(this.stepPress === 0 && this.stepMove === 0 && this.stepRelease === 0)//right click
        {
            this.sendParameters(this.KEY_SET_MOUSE_KEY,2,1);
            this.sendParameters(this.KEY_SET_MOUSE_KEY,2,0);
        }
        else if(this.stepPress === 1 && this.stepMove === 0 && this.stepRelease === 1)//left click
        {
            this.sendParameters(this.KEY_SET_MOUSE_KEY,0,1);
            this.sendParameters(this.KEY_SET_MOUSE_KEY,0,0);
        }
        else if(this.stepPress === 2 && this.stepMove > 3 && this.stepRelease === 2)//left click
        {
            if(this.stepMove < 40)
            {
                this.sendParameters(this.KEY_SET_MOUSE_KEY,0,1);
                this.sendParameters(this.KEY_SET_MOUSE_KEY,0,0);
            }
        }
        else if(this.stepPress === 2 && this.stepMove === 0 && this.stepRelease === 1)//left press
        {
            this.touchTimer = setTimeout(this.touchWaiter.bind(this),200);
            return;
        }
        else if(this.stepPress === 2 && this.stepMove === 0 && this.stepRelease === 2)//double left click
        {
            this.sendParameters(this.KEY_SET_MOUSE_KEY,0,1);
            this.sendParameters(this.KEY_SET_MOUSE_KEY,0,0);
            this.sendParameters(this.KEY_SET_MOUSE_KEY,0,1);
            this.sendParameters(this.KEY_SET_MOUSE_KEY,0,0);
        }
        else if(this.stepPress === 3 && this.stepMove > 3 && this.stepRelease === 3)//double left click
        {
            this.sendParameters(this.KEY_SET_MOUSE_KEY,0,1);
            this.sendParameters(this.KEY_SET_MOUSE_KEY,0,0);
            this.sendParameters(this.KEY_SET_MOUSE_KEY,0,1);
            this.sendParameters(this.KEY_SET_MOUSE_KEY,0,0);
        }

        if(this.isPressed)
            this.sendParameters(this.KEY_SET_MOUSE_KEY,0,0);

        this.stepPress = 0;
        this.stepRelease = 0;
        this.stepMove = 0;
        this.isPressed = false;
    }

    calcCursorPos(x, y)
    {
        var deltaX = this.touchX - x;
        var deltaY = this.touchY - y;

        this.touchX = x;
        this.touchY = y;

        this.cursorPosX = this.cursorPosX - deltaX;
        this.cursorPosY = this.cursorPosY - deltaY;

        if(this.cursorPosX < 0) this.cursorPosX = 0;
        if(this.cursorPosY < 0) this.cursorPosY = 0;
        if(this.crsorPosX > this.canvas.width) this.cursorPosX = this.canvas.width;
        if(this.cursorPosY > this.canvas.height) this.cursorPosY = this.canvas.height;

        this.sendParameters(this.KEY_SET_CURSOR_POS,this.cursorPosX,this.cursorPosY);
        this.updatePositions();
    }

    setupMouse(mouseReceiver)
    {
		if(mouseReceiver)
		{
         	mouseReceiver.addEventListener('mousedown', this.mouseKeyStateChanged.bind(this));
			mouseReceiver.addEventListener('mouseup', this.mouseKeyStateChanged.bind(this));

			mouseReceiver.addEventListener('wheel', this.mouseWheelEvent.bind(this));
			mouseReceiver.addEventListener('mousemove', this.cursorPosChanged.bind(this));

			// mouseReceiver.addEventListener("keydown", this.keyStateChanged.bind(this));
			// mouseReceiver.addEventListener("keyup", this.keyStateChanged.bind(this));
		}
		else
		{
			this.canvas.addEventListener('mousedown', this.mouseKeyStateChanged.bind(this));
			this.canvas.addEventListener('mouseup', this.mouseKeyStateChanged.bind(this));

			this.canvas.addEventListener('wheel', this.mouseWheelEvent.bind(this));
			this.canvas.addEventListener('mousemove', this.cursorPosChanged.bind(this));

			// this.canvas.addEventListener("keydown", this.keyStateChanged.bind(this));
			// this.canvas.addEventListener("keyup", this.keyStateChanged.bind(this));
		}
    }

    mouseKeyStateChanged(event)
    {
        //event.preventDefault();

        var x = event.clientX;
        var y = event.clientY;

        if(event.type == 'mouseup') {
          this.sendParameters(this.KEY_SET_MOUSE_KEY,event.button,0);
        }else if(event.type == 'mousedown') {
          if (!this.checkCanMouseHandle(x, y)) {
            return;
          }
          this.sendParameters(this.KEY_SET_MOUSE_KEY,event.button,1);
        }
    }

    mouseWheelEvent(event)
    {
        //event.preventDefault();

        var x = event.clientX;
        var y = event.clientY;
        if (!this.checkCanMouseHandle(x, y)) {
          return;
        }

        if(event.deltaY > 0)
            this.sendParameters(this.KEY_SET_MOUSE_WHEEL,0,1);
        else this.sendParameters(this.KEY_SET_MOUSE_WHEEL,0,0);
    }

    //设置鼠标位置回调
    SetMousePosCallback(fnCallback) {
	    this.fnMousePosCallback = fnCallback;
    }

    //设置WebSocekt连接回调
    SetWebSocketCallback(fnCallback) {
      this.fnWebSocketCallback = fnCallback;
    }

    checkCanMouseHandle(x,y) {
      let isCanChanged = true;
      if(this.fnMousePosCallback) {
        isCanChanged = this.fnMousePosCallback(x,y);
      }
      return isCanChanged;
    }

    cursorPosChanged(event)
    {
        var x = event.clientX;
        var y = event.clientY;

		if(this.canvasRect==null)
		{
			return;
		}
        this.cursorPosX = this.originWidth / this.canvasRect.w * (x - this.canvasRect.x);
        this.cursorPosY = this.originHeight / this.canvasRect.h * (y - this.canvasRect.y);

        this.sendParameters(this.KEY_SET_CURSOR_POS,this.cursorPosX,this.cursorPosY);
    }

    keyStateChanged(event)
    {
        //event.preventDefault();
        var state = 0;

        if(event.type == 'keydown')
            state = 1;

        this.sendParameters(this.KEY_SET_KEY_STATE,event.keyCode,state);

        if(state === 1)
            this.keyPressedList.push(event.keyCode);
        else
        {
            if(this.keyPressedList.includes(event.keyCode))
                this.keyPressedList.splice(this.keyPressedList.indexOf(event.keyCode,1));
        }
    }

    updateGeometry()
    {
        this.width = window.innerWidth;
        this.height = window.innerHeight;
        var fieldHeight = this.height;

        if(this.keyboard.style.visibility === "visible")
        {
            if(this.width < this.height)
                rect = new HCRect(0,this.height/3*2,this.width,this.height/3);
            else
            {
                rect = this.proportionalResizing(0, this.height/2, this.width, this.height/2, 100, 30);
                rect.y = this.height - rect.h;
            }

            keyboard.style.left = rect.x + "px";
            keyboard.style.top = rect.y + "px";
            keyboard.style.width = rect.w + "px";
            keyboard.style.height = rect.h + "px";
            fieldHeight -= rect.h;
        }

        var rect;

        if(this.width < this.canvas.width || this.height < this.canvas.height)
        {
            rect = this.proportionalResizing(0, 0, this.width, fieldHeight, this.canvas.width, this.canvas.height);
        }
        else
        {
            rect = new HCRect(this.width/2 - this.canvas.width/2,
                            fieldHeight/2 - this.canvas.height/2,
                            this.canvas.width,
                            this.canvas.height);
        }

        this.deltaRect.w = (this.canvasRect.w) * (this.scaleSize / (this.canvasRect.w/100));
        this.deltaRect.h = (this.canvasRect.h) * (this.scaleSize / (this.canvasRect.w/100));

        this.canvasRect.x = rect.x - this.deltaRect.x;
        this.canvasRect.y = rect.y - this.deltaRect.y;
        this.canvasRect.w = rect.w + this.deltaRect.w;
        this.canvasRect.h = rect.h + this.deltaRect.h;

        var rectList = [this.canvas, this.cursorContainer];

        for(var i=0;i<rectList.length;++i)
        {
            rectList[i].style.left = this.canvasRect.x + 'px';
            rectList[i].style.top = this.canvasRect.y + 'px';
            rectList[i].style.width = this.canvasRect.w + 'px';
            rectList[i].style.height = this.canvasRect.h + 'px';
        }
    }

    updatePositions()
    {
        var posX = this.cursorPosX * this.canvasRect.w / this.canvas.width;
        var posY = this.cursorPosY * this.canvasRect.h / this.canvas.height;

        this.cursor.style.left = posX + "px";
        this.cursor.style.top = posY + "px";

        if(this.scaleSize > 0)
        {
            var percentX = 1.0 / this.canvas.width * this.cursorPosX;
            var percentY = 1.0 / this.canvas.height * this.cursorPosY;

            var delta = this.scaleSize * 10;
            var deltaX = (delta * percentX) - (delta / 2);
            var deltaY = (delta * percentY) - (delta / 2);

            this.deltaRect.x = (this.deltaRect.w * percentX) + deltaX;
            this.deltaRect.y = (this.deltaRect.h * percentY) + deltaY;
        }
        else
        {
            this.deltaRect.x = 0;
            this.deltaRect.y = 0;
        }

        this.updateGeometry();
    }

    leavePageEvent()
    {
        var len = this.keyPressedList.length;

        for(var i=0;i<len;++i)
            this.sendParameters(this.KEY_SET_KEY_STATE,this.keyPressedList[i],false);
    }

    setImageParameters(w, h, r)
    {
        this.rectWidth = r;

        if(this.canvas)
        {
            this.canvas.width = w;
            this.canvas.height = h;
            this.updateGeometry();
        }
    }

    setImageData( b64data)
    {
        if(!this.ctx)
            return;

        var image = new Image();

        image.ctx = this.ctx;
        image.width = this.canvas.width;
        image.height = this.canvas.height;

        image.onload = function()
        {
            this.ctx.drawImage(this, 0, 0, this.width,this.height);
        }

        image.src = b64data;
    }

    createCanvas()
    {
		if(this.canvas==null)
		{
			this.canvas = document.createElement('canvas');
			this.canvas.id = 'render_canvas';
			this.canvas.width = this.scaleWidth;
			this.canvas.height = this.scaleHeight;
			this.canvas.style.cssText =
				'position: absolute; \
				width: 100%; \
				height: 100%; \
				z-index: -1000;';

			document.body.append(this.canvas);
		}
		else
		{
			this.canvas.width = this.scaleWidth;
			this.canvas.height = this.scaleHeight;
			this.canvas.style.cssText =
				'position: absolute; \
				width: 100%; \
				height: 100%; \
				z-index: -1000;';
		}
    }

    createCursorField()
    {
        this.cursorField = document.createElement('div');
        this.cursorField.id = 'cursorField';
        this.cursorField.style.cssText =
            'position:absolute; margin:0; \
            padding:0; \
            left:0; \
            top:0; \
            width:100%; \
            height:100%; \
            z-index:-1001;';

        this.cursorContainer = document.createElement('div');
        this.cursorContainer.id = 'cursorContainer';
        this.cursorContainer.style.cssText = "position:absolute; z-index:-1001;";
        this.cursorField.append(this.cursorContainer);

        this.cursor = document.createElement('div');
        this.cursor.id = 'cursor';
        this.cursor.style.cssText =
            'position:absolute; \
            margin:0; \
            padding:0; \
            width:10px; \
            height:10px; \
            z-index:-1002; \
            visibility:hidden;';

        this.cursorContainer.append(this.cursor);

        var svgCursor = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        svgCursor.id = 'svgCursor';
        svgCursor.setAttribute("width", "24");
        svgCursor.setAttribute("height", "24");
        svgCursor.setAttribute ("viewBox", "0 0 24 24");
        svgCursor.innerHTML = '<path d="M0,0 L24,9 L13,13 L9,24 L0,0" stroke="black" stroke-width="1" fill="white"/>';
        svgCursor.style.cssText =
            'display: block; \
            position: absolute; \
            margin: 0; \
            padding: 0; \
            left: 0; \
            top: 0; \
            width: 100%; \
            height: 100%;';

        this.cursor.append(svgCursor);

        document.body.append(this.cursorField);

        this.keyboard = document.createElement('div');
        this.keyboard.id = 'keyboard';
        this.keyboard.isLoaded = false;
        this.keyboard.style.cssText =
            'position: absolute;\
            background: none;\
            z-index: -1003;\
            visibility: hidden;';

        document.body.append(this.keyboard);
    }

    showKeyboard()
    {
        if(!this.keyboard.isLoaded)
            this.sendToXmlHttpRequest('GET', 'keyboard.html', '');

        if(this.keyboard.style.visibility === "visible")
            this.keyboard.style.visibility = "hidden";
        else this.keyboard.style.visibility = "visible";

        this.updateGeometry();
    }

    setKeyboardHtml(text)
    {
        this.keyboard.isLoaded = true;
        this.keyboard.innerHTML = text;

        var keys = this.keyboard.getElementsByClassName('key');

        if(this.isMobilePhone)
        {
            for(var j=0;j<keys.length;++j) {
                keys[j].addEventListener('touchstart', this.keyboardKeyStateChanged.bind(this,keys[j],1));
                keys[j].addEventListener('touchend', this.keyboardKeyStateChanged.bind(this,keys[j],0));
            }
        }
        else
        {
            for(var i=0;i<keys.length;++i) {
                keys[i].addEventListener('mousedown', this.keyboardKeyStateChanged.bind(this,keys[i],1));
                keys[i].addEventListener('mouseup', this.keyboardKeyStateChanged.bind(this,keys[i],0));
            }
        }
    }

    keyboardKeyStateChanged(key,state,event)
    {
        var num = key.getAttribute("num");
        this.sendParameters(this.KEY_SET_KEY_STATE,num,state);
    }

    proportionalResizing(rectX, rectY, rectW, rectH, ratioW, ratioH)
    {
        var sSolution = ((rectW * ratioH) / rectH);

        var sRect = new HCRect(rectX,rectY,rectW,rectH);

        if(sSolution > ratioW) {
            sRect.w = (ratioW * rectH) / ratioH;
            sRect.x = rectX + (rectW / 2) - (sRect.w / 2);
        } else {
            sRect.h = (ratioH * rectW) / ratioW;
            sRect.y = rectY + (rectH / 2) - (sRect.h / 2);
        }

        return sRect;
    }


	sendDataMessage(key, parameter)
    {
        var paramSize =  parameter.length
        var buf = new Uint8Array(paramSize + 6);
        buf[0] = key[0];
        buf[1] = key[1];
        buf[2] = key[2];
        buf[3] = key[3];
        buf[4] = paramSize;
        buf[5] = paramSize >> 8;

        for(var i=0;i<paramSize;++i)
            buf[i + 6] = parameter[i];

        this.sendToSocket(buf);
    }

    startCmdSocket()
    {
        var host=this.remote_ip + ':'+String(this.remote_port);
        let preHost = 'ws://';
        if(GetQtController().inParam.dataUrl.indexOf('https') != -1) {
          preHost = 'wss://';
        }
        this.webCmdSocket = new WebSocket(preHost + host+'/');

	console.log("hcplayer startCmdSocket host:" + host);

        if(!this.webCmdSocket)
        {
            this.showDisconnectMessage();
            return;
        }

        this.webCmdSocket.binaryType = 'arraybuffer';
        this.webCmdSocket.onopen = this.onCmdSocketOpen.bind(this);
        this.webCmdSocket.onmessage = this.onCmdData.bind(this);
        this.webCmdSocket.onclose = this.showDisconnectMessage.bind(this);
    }

    startCmdSession()
    {
        if(this.webCmdSocket)
        {
            if(this.webCmdSocket.readyState === WebSocket.OPEN)
            {
                this.isConnected = true;
                this.removeDisconnectMessage();

				if(this.login!=null && this.pass!=null)
				{
					this.doLogin(this.login,this.pass,this.nonce);
				}
            }
            else
            {
                this.showDisconnectMessage();
                console.log("try 'startCmdSession' but socket is not connected yet");
            }
        }
    }

    onCmdSocketOpen()
    {
        setTimeout(this.startCmdSession.bind(this),1500);
    }

    onCmdData(event)
    {
        var data = event.data;
        var dataArray = new Uint8Array(data);
        var activeBuf = new Uint8Array(dataArray.length + this.cmdTmp.length);
        activeBuf.set(this.cmdTmp, 0);
        activeBuf.set(dataArray, this.cmdTmp.length);

        var size = activeBuf.length;

        if(size < this.REQUEST_MIN_SIZE)
            return;

        var dataStep = 0;

        for(var i=0;i<size;++i)
        {
            var command = activeBuf.subarray(dataStep, dataStep+this.COMMAD_SIZE);
            var dataSize = this.uint16FromArray(activeBuf.subarray(dataStep + this.COMMAD_SIZE, dataStep + this.COMMAD_SIZE + 2));

            if(size >= (dataStep + this.COMMAD_SIZE + 2 + dataSize))
            {
                var payload = activeBuf.subarray(dataStep + this.COMMAD_SIZE + 2, dataStep + this.COMMAD_SIZE + 2 + dataSize);
                dataStep += this.COMMAD_SIZE + 2 + dataSize;

                this.newCmdData(command,payload);

                i = dataStep;
            }
            else
            {
                this.cmdTmp = activeBuf.subarray(dataStep, dataStep + (size - dataStep));
                break;
            }
        }
    }

	uint8ToStr(fileData)
	{
		var dataString = "";
		for (var i = 0; i < fileData.length; i++) {
			dataString += String.fromCharCode(fileData[i]);
		}

		return dataString
	}

	startHeartbeat()
	{
		var pThis=this;
		setInterval(function()
		{
			pThis.sendParameters(pThis.KEY_HEARTBEAT,0,0);
		},30000);
	}


	toCharCode(array)
	{
		var res = '';
		var chunk = 8 * 1024;
		var i;
		for (i = 0; i < array.length / chunk; i++) {
		  res += String.fromCharCode.apply(null, array.slice(i * chunk, (i + 1) * chunk));
		}
		res += String.fromCharCode.apply(null, array.slice(i * chunk));
		return res;
	}

    sendToSocket(data)
    {
        if(this.isConnected)
        {
            this.webCmdSocket.binaryType = 'arraybuffer';
            this.webCmdSocket.send(data);
        }
        else console.log("try 'sendToSocket' but socket is not connected yet");
    }

    sendTextToSocket(text)
    {
        if(this.isConnected)
        {
            this.webCmdSocket.binaryType = 'blob';
            this.webCmdSocket.send(text);
        }
    }

    setLoginClass(lClass)
    {
        if(lClass)
        {
            this.loginClass = lClass;
            lClass.setPlayer(this);
        }
    }

    sendParameters(key, param1, param2)
    {
        var posSize = this.arrayFromUint16(4);
        var posXBuf = this.arrayFromUint16(param1);
        var posYBuf = this.arrayFromUint16(param2);

        var buf = new Uint8Array(10);
        buf[0] = key[0];
        buf[1] = key[1];
        buf[2] = key[2];
        buf[3] = key[3];
        buf[4] = posSize[0];
        buf[5] = posSize[1];
        buf[6] = posXBuf[0];
        buf[7] = posXBuf[1];
        buf[8] = posYBuf[0];
        buf[9] = posYBuf[1];

        this.sendToSocket(buf);
    }

    uint16FromArray(buf)
    {
        if(buf.length === 2)
        {
            var number = buf[0] | buf[1] << 8;
            return number;
        }
        else return 0x0000;
    }

    arrayFromUint16(num)
    {
        var buf = new Uint8Array(2);
        buf[0] = num;
        buf[1] = num >> 8;
        return buf;
    }

    startXmlHttpRequest()
    {
        this.xmlHttpRequest = new XMLHttpRequest();
        this.xmlHttpRequest.onload = this.readFromXmlHttpRequest.bind(this);
    }

    readFromXmlHttpRequest()
    {
        var data = this.xmlHttpRequest.responseText;
        var url = this.xmlHttpRequest.responseURL;

        if(url.includes('keyboard.html'))
            this.setKeyboardHtml(data);
    }

    sendToXmlHttpRequest(method, request, data)
    {
        this.xmlHttpRequest.open(method, request);
        this.xmlHttpRequest.send(data);
    }

    showDisconnectMessage()
    {
        if(window.qtController.inParam.clientType == 2) {
          //微客户端
          return;
        }
        if(this.fnWebSocketCallback)
        {
          this.fnWebSocketCallback(CODE_ERROR_CMD_WEBSOCKET,'信令WebSocekt连接失败！');
          return;
        }

        if(this.messageField)
            return;

        this.messageField = document.createElement('div');
        this.messageField.id = 'messageField';
        this.messageField.style.cssText = 'position:absolute;\
            margin:0; \
            padding:0; \
            left:0; \
            top:0; \
            width:100%; \
            height:100%; \
            z-index:10; \
            background: #111;';

        this.messageLabel = document.createElement('div');
        this.messageLabel.id = 'messageLabel';
        this.messageLabel.innerHTML = 'ERROR: No connection to server!';
        this.messageLabel.style.cssText = 'position: absolute; \
            background: none; \
            color: #b00; \
            font: 14px monospace; \
            text-align: center; \
            letter-spacing: 1px; \
            line-height: 30px; \
            border: none; \
            top: calc(50% - 15px); \
            left: calc(50% - 125px); \
            width: 300px; \
            height: 30px;';
        this.messageField.append(this.messageLabel);

        this.messageImg = document.createElement("img");
        this.messageImg.id = 'messageImg';
        this.messageImg.src = "favicon.ico";
        this.messageImg.style.cssText = 'position: absolute; \
            border: none; \
            top: calc(50% - 15px); \
            left: calc(50% - 160px); \
            width: 30px; \
            height: 30px;';
        this.messageField.append(this.messageImg);

        document.body.append(this.messageField);
    }

    removeDisconnectMessage()
    {
        if(this.messageField)
        {
            this.messageField.remove();
            this.messageField = null;
        }
    }

   openPlayer ()
   {
        if(this.canvas==null)
        {
	       return;
        }

        this.gl = this.canvas.getContext("webgl") || this.canvas.getContext("experimental-webgl");

		if (!this.gl) {
			alert("WebGL not supported,please add params:--enable-webgl --ignore-gpu-blacklist.");
			return;
		}

		var gl = this.gl;
		gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
		var program = gl.createProgram();
		var vertexShaderSource = [
			"attribute highp vec4 aVertexPosition;",
			"attribute vec2 aTextureCoord;",
			"varying highp vec2 vTextureCoord;",
			"void main(void) {",
			" gl_Position = aVertexPosition;",
			" vTextureCoord = aTextureCoord;",
			"}"
		].join("\n");
		var vertexShader = gl.createShader(gl.VERTEX_SHADER);
		gl.shaderSource(vertexShader, vertexShaderSource);
		gl.compileShader(vertexShader);
		var fragmentShaderSource = [
			"precision highp float;",
			"varying lowp vec2 vTextureCoord;",
			"uniform sampler2D YTexture;",
			"uniform sampler2D UTexture;",
			"uniform sampler2D VTexture;",
			"const mat4 YUV2RGB = mat4",
			"(",
			" 1.1643828125, 0, 1.59602734375, -.87078515625,",
			" 1.1643828125, -.39176171875, -.81296875, .52959375,",
			" 1.1643828125, 2.017234375, 0, -1.081390625,",
			" 0, 0, 0, 1",
			");",
			"void main(void) {",
			" gl_FragColor = vec4( texture2D(YTexture, vTextureCoord).x, texture2D(UTexture, vTextureCoord).x, texture2D(VTexture, vTextureCoord).x, 1) * YUV2RGB;",
			"}"
		].join("\n");

		var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
		gl.shaderSource(fragmentShader, fragmentShaderSource);
		gl.compileShader(fragmentShader);
		gl.attachShader(program, vertexShader);
		gl.attachShader(program, fragmentShader);
		gl.linkProgram(program);
		gl.useProgram(program);
		if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
			console.log("[ER] Shader link failed.");
		}
		var vertexPositionAttribute = gl.getAttribLocation(program, "aVertexPosition");
		gl.enableVertexAttribArray(vertexPositionAttribute);
		var textureCoordAttribute = gl.getAttribLocation(program, "aTextureCoord");
		gl.enableVertexAttribArray(textureCoordAttribute);

		var verticesBuffer = gl.createBuffer();
		gl.bindBuffer(gl.ARRAY_BUFFER, verticesBuffer);
		gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, -1.0, -1.0, 0.0]), gl.STATIC_DRAW);
		gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
		var texCoordBuffer = gl.createBuffer();
		gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
		// gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1.0, 1.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0]), gl.STATIC_DRAW);
		gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0]), gl.STATIC_DRAW);   //图像倒转
		gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);

		gl.y = new HCTexture(gl);
		gl.u = new HCTexture(gl);
		gl.v = new HCTexture(gl);
		gl.y.bind(0, program, "YTexture");
		gl.u.bind(1, program, "UTexture");
		gl.v.bind(2, program, "VTexture");
	}

    renderFrame (videoFrame, width, height, uOffset, vOffset)
	{
		if (!this.gl) {
			console.log("[ER] Render frame failed due to WebGL not supported.");
			return;
		}

		var gl = this.gl;
		gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
		gl.clearColor(0.0, 0.0, 0.0, 0.0);
		gl.clear(gl.COLOR_BUFFER_BIT);

		gl.y.fill(width, height, videoFrame.subarray(0, uOffset));
		gl.u.fill(width >> 1, height >> 1, videoFrame.subarray(uOffset, uOffset + vOffset));
		gl.v.fill(width >> 1, height >> 1, videoFrame.subarray(uOffset + vOffset, videoFrame.length));

		gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
	}

	onVideoData(event)
	{
		var obj=event.data;
		if(obj.type==1)  //worker ready
		{
			this.webCmdSocket.send(this.KEY_BROWSER_IMAGE);
            console.log("KEY_BROWSER_IMAGE sended");
		}
		else if(obj.type==2)
		{
			this.decodeWorker.postMessage({type:2,dataHost:this.dataHost});
		}
		else
		{
			var width = obj.width;
			var height = obj.height;
			var yLength = width * height;
			var uvLength = (width / 2) * (height / 2);

			this.renderFrame(obj.data, width, height, yLength, uvLength);
		}
	}


	initCanvas(originWidth,originHeight)
    {
		console.log("hcplayer initCanvas originWidth:"+originWidth+",originHeight:"+originHeight);

		this.originWidth=originWidth;
		this.originHeight=originHeight;

		this.canvasRect = new HCRect(0,0,this.scaleWidth,this.scaleHeight);

		this.createCanvas();
		this.createCursorField();

		//window.addEventListener("contextmenu", function(event){event.preventDefault();});
		window.addEventListener('resize', this.updateGeometry.bind(this));
		window.addEventListener("blur", this.leavePageEvent.bind(this));

		if(this.bSetupMouse)
		{
			if(this.isMobilePhone)
			{
			   this.setupTouch();
			}
			else
			{
			   this.setupMouse();
			}
		}

		this.updateGeometry();
    }


	doLogin(user,pass,nonce)
	{
	    if(1)
		{
			var pass64 = hex_md5(pass);
			var concatSecond =user+","+pass64;
			var binaryString =concatSecond;

			var response = new Uint8Array(binaryString.length);
			for(var i=0;i<response.length;++i)
				response[i] = binaryString.charCodeAt(i);

			this.sendDataMessage(this.KEY_SET_AUTH_REQUEST, response);
		}
		else
		{
			var concatFirst = btoa(rstr_md5(user +pass));
			var concatSecond = btoa(rstr_md5(concatFirst + nonce));

			var binaryString = user+","+atob(concatSecond);

			var response = new Uint8Array(binaryString.length);
			for(var i=0;i<response.length;++i)
				response[i] = binaryString.charCodeAt(i);

			this.sendDataMessage(this.KEY_SET_AUTH_REQUEST, response);
		}

	}

	initEnv()
	{
		if(this.isInitEnv)
		{
			return;
		}
		this.initCanvas(this.originWidth,this.originHeight);

		this.createExtraKeysHtml();

		this.sendParameters(this.KEY_SET_SCALE_SIZE,this.scaleWidth,this.scaleHeight);



		if(this.isHCNative)
		{
			console.log("browserInfo=================================================:hcbrowser");
			this.webCmdSocket.send(this.KEY_GET_IMAGE);
		}
		else
		{
			this.dataHost=this.remote_ip+":"+String(this.remote_port+3);

	    	console.log("hcplayer initEnv host:"+this.dataHost);

			this.decodeWorker = new Worker(this.workerPath+"hcworker.js");
		    this.decodeWorker.postMessage({type:1,dataHost:this.dataHost});
		    this.decodeWorker.addEventListener('message',event => {this.onVideoData(event);});
		    this.openPlayer();

		}
        this.isInitEnv=true;

		this.startHeartbeat();
	}


    init(params)
    {

       this.remote_ip=params.ip;
       this.remote_port=params.port;
       this.scaleWidth=params.width;
       this.scaleHeight=params.height;
       this.login=params.login;
       this.pass=params.pass;
       this.isHCNative=params.isNative;

	  // if(this.getBrowserInfo()=="hcclouder")
	  // {

	  // }

       if(params.bSetupMouse!=null)
       {
          this.bSetupMouse=params.bSetupMouse
       }
       else
       {
          this.bSetupMouse=true;
       }


       if(params.workerPath!=null)
       {
          this.workerPath=params.workerPath;
       }

       if(params.canvas)
       {
          this.canvas=params.canvas;
       }


       this.startCmdSocket();

       console.log("hcplayer init success!");

    }

    newCmdData(cmd, data)
    {
		if(cmd.length !== 4)
		{
			console.log("newCmdData cmd.length<4.");
			return;
		}

		var command = cmd.toString();

		if(command === this.KEY_SET_NONCE)
		{
			if(this.loginClass)
			{
				this.loginClass.createLoginHtml();
				this.loginClass.setNonce(btoa(String.fromCharCode.apply(null, data)));
			}
			console.log("newCmdData KEY_SET_NONCE.");
		}
		else if(command === this.KEY_AUTO_LOGIN)
		{
			this.isConnected = true;
			if(this.loginClass)
			{
				var length = this.uint16FromArray(data.subarray(0,2));
				var nonce= data.subarray(2,2+length);

				var str=this.uint8ToStr(data.subarray(2+length));
				var dataArray = str.split(':');

				this.loginClass.setNonce(btoa(String.fromCharCode.apply(null, nonce)));

				this.doLogin(dataArray[0],dataArray[1],this.loginClass.nonce);


			}
			console.log("newCmdData KEY_AUTO_LOGIN.");
		}
		else if(command === this.KEY_SET_AUTH_RESPONSE)
		{

                        var response = this.uint16FromArray(data.subarray(0,2));
                        var originWidth = this.uint16FromArray(data.subarray(2,4));
                        var originHeight = this.uint16FromArray(data.subarray(4,6));

                        console.log("newCmdData KEY_SET_AUTH_RESPONSE.response="+response);

			if(response === 1)
			{

				if(this.loginClass)
				{
					this.loginClass.removeLoginHtml();
				}

				this.initEnv();

			}
			else
			{
				if(this.loginClass)
					this.loginClass.showWrongRequest(response);
			}

		}
		else if(command === this.KEY_IMAGE_PARAM)
		{
			var imageWidth = this.uint16FromArray(data.subarray(0,2));
			var imageHeight = this.uint16FromArray(data.subarray(2,4));
			var rectWidth = this.uint16FromArray(data.subarray(4,6));


			this.setImageParameters(imageWidth,imageHeight,rectWidth);
		}
		else if(command === this.KEY_CHECK_AUTH_RESPONSE)
		{
			var uuid = data.subarray(0,16);
			var name = data.subarray(16,data.length);
			var nameString = String.fromCharCode.apply(null, name);

			if(this.loginClass)
				this.loginClass.addDesktopButton(uuid, nameString);
			console.log("newCmdData KEY_CHECK_AUTH_RESPONSE.");

		}
		else if(command === this.KEY_SET_NAME)
		{
			var titleName = String.fromCharCode.apply(null, data);

		  //  if(titleName)
		  //      document.title = titleName;
	               console.log("newCmdData KEY_SET_NAME.");
		}
		else
		{
			console.log("newData:",command.toString(),command,data);
		}
	}

	close() {
	  if(this.webCmdSocket) {
	    this.webCmdSocket.close();
    }
  }


}
